<!DOCTYPE html>
<html prefix="
og: http://ogp.me/ns#
article: http://ogp.me/ns/article#
" lang="zh_cn">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Nikola Internals | vincent blog</title>
<link href="../../assets/css/all-nocdn.css" rel="stylesheet" type="text/css">
<link rel="alternate" type="application/rss+xml" title="RSS" href="../../rss.xml">
<link rel="canonical" href="https://wisonwang.github.io/pages/internals/">
<!--[if lt IE 9]><script src="/assets/js/html5.js"></script><![endif]--><meta name="author" content="The Nikola Team">
<meta property="og:site_name" content="vincent blog">
<meta property="og:title" content="Nikola Internals">
<meta property="og:url" content="https://wisonwang.github.io/pages/internals/">
<meta property="og:description" content="Nikola Internals
When trying to guide someone into adding a feature in Nikola, it hit me that
while the way it's structured makes sense to me it is far from obvious.
So, this is a short document expla">
<meta property="og:type" content="article">
<meta property="article:published_time" content="2012-03-30T23:00:00-03:00">
<meta name="author" content="The Nikola Team">
<meta property="og:site_name" content="vincent blog">
<meta property="og:title" content="Nikola Internals">
<meta property="og:url" content="https://wisonwang.github.io/pages/internals/">
<meta property="og:description" content="Nikola Internals
When trying to guide someone into adding a feature in Nikola, it hit me that
while the way it's structured makes sense to me it is far from obvious.
So, this is a short document expla">
<meta property="og:type" content="article">
<meta property="article:published_time" content="2012-03-30T23:00:00-03:00">
</head>
<body>
    <section class="social"><ul>
<li><a href="../../index.html" title="Home"><i class="icon-home"></i></a></li>
            <li><a href="../../archive.html" title="文章存档"><i class="icon-archive"></i></a></li>
            <li><a href="../../categories/" title="标签"><i class="icon-tags"></i></a></li>
            <li><a href="../about" title="About"><i class="icon-about"></i></a></li>
            <li><a href="../../rss.xml" title="RSS 源"><i class="icon-rss"></i></a></li>
            <li><a href="https://twitter.com/wisonwang" title="My Twitter"><i class="icon-twitter"></i></a></li>
            <li><a href="https://github.com/wisonwang" title="My Github"><i class="icon-github"></i></a></li>

        </ul></section><section class="page-content"><div class="content" rel="main">
<article class="storypage" itemscope="itemscope" itemtype="http://schema.org/Article"><header><h1 class="p-name entry-title" itemprop="headline name">Nikola Internals</h1>

        
    </header><div itemprop="articleBody text">
    <div>
<div class="section" id="nikola-internals">
<h2>Nikola Internals</h2>
<p class="lead">When trying to guide someone into adding a feature in Nikola, it hit me that
while the way it's structured makes sense <strong>to me</strong> it is far from obvious.</p>
<p>So, this is a short document explaining what each piece of Nikola does and
how it all fits together.</p>
<dl class="docutils">
<dt>Nikola is a Pile of Plugins</dt>
<dd>
<p class="first">Most of Nikola is implemented as plugins using <a class="reference external" href="http://yapsy.sourceforge.net/">Yapsy</a>.
You can ignore that they are plugins and just think of them as regular python
modules and packages with a funny little <tt class="docutils literal">.plugin</tt> file next to them.</p>
<p>So, 90% of the time, what you want to do is either write a new plugin or extend
an existing one.</p>
<p>There are several kinds of plugins, all implementing interfaces defined in
<tt class="docutils literal">nikola/plugin_categories.py</tt> and documented in
<a class="reference external" href="https://getnikola.com/extending.html">Extending Nikola</a></p>
<p class="last">If your plugin has a dependency, please make sure it doesn't make Nikola
throw an exception when the dependency is missing. Try to fail gracefully
with an informative message.</p>
</dd>
<dt>Commands are plugins</dt>
<dd>When you use <tt class="docutils literal">nikola foo</tt> you are using the plugin <tt class="docutils literal">command/foo</tt>. Those are
used to extend Nikola's command line. Their interface is defined in the <tt class="docutils literal">Command</tt>
class. They take options and arguments and do whatever you want, so go wild.</dd>
<dt>The <tt class="docutils literal">build</tt> command is special</dt>
<dd>The <tt class="docutils literal">build</tt> command triggers a whole lot of things, and is the core of Nikola
because it's the one that you use to build sites. So it deserves its own section.</dd>
</dl>
<div class="section" id="the-build-command">
<h3>The Build Command</h3>
<p>Nikola's goal is similar, deep at heart, to a Makefile. Take sources, compile them
into something, in this case a website. Instead of a Makefile, Nikola uses
<a class="reference external" href="http://pydoit.org">doit</a></p>
<p>Doit has the concept of "tasks". The 1 minute summary of tasks is that they have:</p>
<dl class="docutils">
<dt>actions</dt>
<dd>What the task <strong>does</strong>. For example, convert a markdown document into HTML.</dd>
<dt>dependencies</dt>
<dd>If this file changes, then we need to redo the actions. If this configuration
option changes, redo it, etc.</dd>
<dt>targets</dt>
<dd>Files that the action generates. No two actions can have the same targets.</dd>
<dt>basename:name</dt>
<dd>Each task is identified by either a name or a basename:name pair.</dd>
</dl>
<div class="sidebar">
<p class="first sidebar-title">More about tasks</p>
<p class="last">If you ever want to do your own tasks, you really should read the doit
<a class="reference external" href="http://pydoit.org/tasks.html">documentation on tasks</a></p>
</div>
<p>So, what Nikola does, when you use the build command, is to read the
configuration <tt class="docutils literal">conf.py</tt> from the current folder, instantiate
the <tt class="docutils literal">Nikola</tt> class, and have it generate a whole list of tasks for doit
to process. Then doit will decide which tasks need doing, and do them, in
the right order.</p>
<p>The place where the tasks are generated is in <tt class="docutils literal">Nikola.gen_tasks</tt>, which collects tasks
from all the plugins inheriting <tt class="docutils literal">BaseTask</tt>, massages them a bit, then passes them
to doit.</p>
<p>So, if you want things to happen on <tt class="docutils literal">build</tt> you want to create a Task plugin, or extend
one of the existing ones.</p>
<div class="sidebar">
<p class="first sidebar-title">Tests</p>
<p class="last">While Nikola is not a hardcore TDD project, we like tests. So, please add them if you can.
You can write unit tests or integration tests. (Doctests are not supported
anymore due to fragility.)</p>
</div>
</div>
<div class="section" id="posts-and-pages">
<h3>Posts and Pages</h3>
<p>Nikola has a concept of posts and pages. Both are more or less the same thing, except
posts are added into RSS feeds and pages are not. All of them are in a list called
"the timeline" formed by objects of class <tt class="docutils literal">Post</tt>.</p>
<p>When you are creating a task that needs the list of posts and/or pages (for example,
the RSS creation plugin) on task execution time, your plugin should call <tt class="docutils literal">self.site.scan_posts()</tt>
in <tt class="docutils literal">gen_tasks</tt> to ensure the timeline is created and available in
<tt class="docutils literal">self.site.timeline</tt>. You should not modify the timeline, because it will cause consistency issues.</p>
<div class="sidebar">
<p class="first sidebar-title">scan_posts</p>
<p class="last">The <tt class="docutils literal">Nikola.scan_posts</tt> function can be used in plugins to force the
timeline creation, for example, while creating the tasks.</p>
</div>
<p>Your plugin can use the timeline to generate "stuff" (technical term). For example,
Nikola comes with plugins that use the timeline to create a website (surprised?).</p>
<p>The workflow included with nikola is as follows (incomplete!):</p>
<ol class="arabic simple">
<li>The post is assigned a compiler based on its extension and the <tt class="docutils literal">COMPILERS</tt> option.</li>
<li>The compiler is applied to the post data and a "HTML fragment" is produced. That
fragment is stored in a cache (the <tt class="docutils literal">posts</tt> plugin).</li>
<li>The configured theme has templates (and a template engine), which are applied to the post's
HTML fragment and metadata (the <tt class="docutils literal">pages</tt> plugin).</li>
<li>The original sources for the post are copied to some accessible place (the <tt class="docutils literal">sources</tt> plugin).</li>
<li>If the post is tagged, some pages and RSS feeds for each tag are updated (the <tt class="docutils literal">tags</tt> plugin).</li>
<li>If the post is new, it's included in the blog's RSS feed (the <tt class="docutils literal">rss</tt> plugin).</li>
<li>The post is added in the right place in the index pages for the blog (the <tt class="docutils literal">indexes</tt> plugin).</li>
<li>CSS/JS/Images for the theme are put in the right places (the <tt class="docutils literal">copy_assets</tt> and <tt class="docutils literal">bundles</tt> plugins).</li>
<li>A File describing the whole site is created (the <tt class="docutils literal">sitemap</tt> plugin).</li>
</ol>
<p>You can add whatever you want to that list: just create a plugin for it.</p>
<p>You can also expand Nikola's capabilities at several points:</p>
<dl class="docutils">
<dt>compilers</dt>
<dd>Nikola supports a variety of markups. If you want to add another one, you need to create
a <tt class="docutils literal">Compiler</tt> plugin.</dd>
<dt>templates</dt>
<dd>Nikola's themes can use Jinja2 or Mako templates. If you prefer another template system,
you have to create a <tt class="docutils literal">TemplateSystem</tt> plugin.</dd>
<dt>themes</dt>
<dd>To change how the generated site looks, you can create custom themes.</dd>
</dl>
<p>And of course, you can also replace or extend each of the existing plugins.</p>
</div>
</div>
<div class="section" id="nikola-architecture">
<h2>Nikola Architecture</h2>
<a class="reference external image-reference" href="https://getnikola.com/images/architecture.png"><img alt="https://getnikola.com/images/architecture.thumbnail.png" src="https://getnikola.com/images/architecture.thumbnail.png"></a>
</div>
</div>
    </div>
</article><footer id="footer"><p>Contents © 2020         <a href="mailto:fangfu2012@gmail.com">vincent wang</a> - Powered by         <a href="https://getnikola.com" rel="nofollow">Nikola</a>         </p>
            
        </footer>
</div>
    </section><script src="../../assets/js/all-nocdn.js" type="text/javascript"></script><script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script><script type="text/x-mathjax-config">
    MathJax.Hub.Config({tex2jax: {inlineMath: [['$latex ','$'], ['\\(','\\)']]}});
    </script><script type="text/javascript">
            $(function(){
                $('.timeago').timeago();
            });
        </script>
</body>
</html>
